Palieliniet sava Python koda veiktspÄju par vairÄkÄm kÄrtÄm. Å Ä« izsmeļoÅ”Ä rokasgrÄmata pÄta SIMD, vektorizÄciju, NumPy un uzlabotas bibliotÄkas vispasaules izstrÄdÄtÄjiem.
VeiktspÄjas atklÄÅ”ana: VisaptveroÅ”a rokasgrÄmata par Python SIMD un vektorizÄciju
SkaitļoÅ”anas pasaulÄ Ätrums ir vissvarÄ«gÄkais. NeatkarÄ«gi no tÄ, vai esat datu zinÄtnieks, kas apmÄca maŔīnmÄcīŔanÄs modeli, finanÅ”u analÄ«tiÄ·is, kas veic simulÄciju, vai programmatÅ«ras inženieris, kas apstrÄdÄ lielus datu kopumus, jÅ«su koda efektivitÄte tieÅ”i ietekmÄ produktivitÄti un resursu patÄriÅu. Python, kas ir slavens ar savu vienkÄrŔību un lasÄmÄ«bu, ir labi zinÄms Ahileja papÄdis: tÄ veiktspÄja skaitļoÅ”anas ziÅÄ intensÄ«vos uzdevumos, Ä«paÅ”i tajos, kas saistÄ«ti ar cikliem. Bet ko darÄ«t, ja jÅ«s varÄtu vienlaicÄ«gi veikt operÄcijas ar veselÄm datu kolekcijÄm, nevis ar vienu elementu vienlaicÄ«gi? Tas ir vektorizÄtas skaitļoÅ”anas solÄ«jums, paradigma, ko darbina CPU funkcija, ko sauc par SIMD.
Å Ä« rokasgrÄmata jÅ«s iepazÄ«stinÄs ar Single Instruction, Multiple Data (SIMD) operÄciju un vektorizÄcijas pasauli Python. MÄs ceļosim no CPU arhitektÅ«ras pamatjÄdzieniem lÄ«dz jaudÄ«gu bibliotÄku, piemÄram, NumPy, Numba un Cython, praktiskai pielietoÅ”anai. MÅ«su mÄrÄ·is ir sniegt jums, neatkarÄ«gi no jÅ«su Ä£eogrÄfiskÄs atraÅ”anÄs vietas vai izglÄ«tÄ«bas, zinÄÅ”anas, lai jÅ«su lÄno, ciklisko Python kodu pÄrvÄrstu par ļoti optimizÄtÄm, augstas veiktspÄjas lietojumprogrammÄm.
Pamats: CPU arhitektūras un SIMD izpratne
Lai patiesi novÄrtÄtu vektorizÄcijas spÄku, mums vispirms ir jÄieskatÄs, kÄ darbojas mÅ«sdienu centrÄlais procesors (CPU). SIMD burvÄ«ba nav programmatÅ«ras triks; tÄ ir aparatÅ«ras iespÄja, kas ir revolucionÄjusi skaitlisko skaitļoÅ”anu.
No SISD uz SIMD: paradigmas maiÅa skaitļoÅ”anÄ
Daudzus gadus dominÄjoÅ”ais skaitļoÅ”anas modelis bija SISD (Single Instruction, Single Data). IedomÄjieties Å”efpavÄru, kas rÅ«pÄ«gi smalcina vienu dÄrzeni vienlaicÄ«gi. Å efpavÄram ir viena instrukcija ("sasmalcinÄt") un viÅÅ” darbojas ar vienu datu gabalu (vienu burkÄnu). Tas ir lÄ«dzÄ«gs tradicionÄlam CPU kodolam, kas katrÄ ciklÄ izpilda vienu instrukciju ar vienu datu gabalu. VienkÄrÅ”s Python cikls, kas saskaita skaitļus no diviem sarakstiem pa vienam, ir ideÄls SISD modeļa piemÄrs:
# Conceptual SISD operation
result = []
for i in range(len(list_a)):
# One instruction (add) on one piece of data (a[i], b[i]) at a time
result.append(list_a[i] + list_b[i])
Å Ä« pieeja ir secÄ«ga un rada ievÄrojamas Python interpretatora izmaksas katrai iterÄcijai. Tagad iedomÄjieties, ka Å”efpavÄram tiek dota specializÄta maŔīna, kas ar vienu sviras vilcienu var vienlaicÄ«gi sasmalcinÄt visu Äetru burkÄnu rindu. TÄ ir SIMD (Single Instruction, Multiple Data) bÅ«tÄ«ba. CPU izdod vienu instrukciju, bet tÄ darbojas ar vairÄkiem datu punktiem, kas ir iepakoti kopÄ Ä«paÅ”Ä, platÄ reÄ£istrÄ.
KÄ SIMD darbojas mÅ«sdienu CPU
MÅ«sdienu CPU no ražotÄjiem, piemÄram, Intel un AMD, ir aprÄ«koti ar Ä«paÅ”iem SIMD reÄ£istriem un instrukciju kopumiem, lai veiktu Ŕīs paralÄlÄs operÄcijas. Å ie reÄ£istri ir daudz plaÅ”Äki nekÄ vispÄrÄjas nozÄ«mes reÄ£istri un vienlaicÄ«gi var glabÄt vairÄkus datu elementus.
- SIMD ReÄ£istri: Tie ir lieli aparatÅ«ras reÄ£istri CPU. To izmÄri laika gaitÄ ir attÄ«stÄ«juÅ”ies: 128 bitu, 256 bitu un tagad 512 bitu reÄ£istri ir izplatÄ«ti. PiemÄram, 256 bitu reÄ£istrs var glabÄt astoÅus 32 bitu peldoÅ”Ä punkta skaitļus vai Äetrus 64 bitu peldoÅ”Ä punkta skaitļus.
- SIMD Instrukciju kopumi: CPU ir Ä«paÅ”as instrukcijas darbam ar Å”iem reÄ£istriem. JÅ«s, iespÄjams, esat dzirdÄjuÅ”i Å”os akronÄ«mus:
- SSE (Streaming SIMD Extensions): VecÄks 128 bitu instrukciju kopums.
- AVX (Advanced Vector Extensions): 256 bitu instrukciju kopums, kas piedÄvÄ ievÄrojamu veiktspÄjas pieaugumu.
- AVX2: AVX paplaÅ”inÄjums ar vairÄk instrukcijÄm.
- AVX-512: JaudÄ«gs 512 bitu instrukciju kopums, kas atrodams daudzos mÅ«sdienu serveru un augstÄkÄs klases galddatoru CPU.
VizualizÄsim to. PieÅemsim, ka mÄs vÄlamies saskaitÄ«t divus masÄ«vus, `A = [1, 2, 3, 4]` un `B = [5, 6, 7, 8]`, kur katrs skaitlis ir 32 bitu vesels skaitlis. CPU ar 128 bitu SIMD reÄ£istriem:
- CPU ielÄdÄ `[1, 2, 3, 4]` SIMD reÄ£istrÄ 1.
- CPU ielÄdÄ `[5, 6, 7, 8]` SIMD reÄ£istrÄ 2.
- CPU izpilda vienu vektorizÄtu "add" instrukciju (`_mm_add_epi32` ir reÄlas instrukcijas piemÄrs).
- VienÄ pulksteÅa ciklÄ aparatÅ«ra paralÄli veic Äetras atseviŔķas saskaitīŔanas: `1+5`, `2+6`, `3+7`, `4+8`.
- RezultÄts, `[6, 8, 10, 12]`, tiek saglabÄts citÄ SIMD reÄ£istrÄ.
Tas ir 4x Ätruma pieaugums salÄ«dzinÄjumÄ ar SISD pieeju pamata aprÄÄ·inam, pat neskaitot milzÄ«go instrukciju nosÅ«tīŔanas un cikla izmaksu samazinÄÅ”anu.
VeiktspÄjas atŔķirÄ«ba: skalÄrÄs un vektoru operÄcijas
TradicionÄlÄs, pa vienam elementam veiktÄs operÄcijas termins ir skalÄra operÄcija. OperÄcija ar visu masÄ«vu vai datu vektoru ir vektoru operÄcija. VeiktspÄjas atŔķirÄ«ba nav smalka; tÄ var bÅ«t vairÄku lielumu kÄrtu.
- SamazinÄtas izmaksas: PythonÄ katra cikla iterÄcija ietver izmaksas: cikla nosacÄ«juma pÄrbaudi, skaitÄ«tÄja palielinÄÅ”anu un operÄcijas nosÅ«tīŔanu caur interpretatoru. Vienai vektora operÄcijai ir tikai viena nosÅ«tīŔana, neatkarÄ«gi no tÄ, vai masÄ«vam ir tÅ«kstotis vai miljons elementu.
- AparatÅ«ras paralÄlisms: KÄ mÄs esam redzÄjuÅ”i, SIMD tieÅ”i izmanto paralÄlÄs apstrÄdes vienÄ«bas viena CPU kodola ietvaros.
- Uzlabota keÅ”atmiÅas lokalitÄte: VektorizÄtÄs operÄcijas parasti lasa datus no blakus esoÅ”iem atmiÅas blokiem. Tas ir ļoti efektÄ«vi CPU keÅ”atmiÅas sistÄmai, kas ir paredzÄta datu iepriekÅ”Äjai ielÄdei secÄ«gÄs daļÄs. NejauÅ”i piekļuves modeļi ciklos var izraisÄ«t biežas "keÅ”atmiÅas kļūmes", kas ir neticami lÄnas.
Pythoniskais veids: vektorizÄcija ar NumPy
AparatÅ«ras izpratne ir aizraujoÅ”a, taÄu jums nav jÄraksta zema lÄ«meÅa asemblera kods, lai izmantotu tÄs jaudu. Python ekosistÄmÄ ir fenomenÄla bibliotÄka, kas padara vektorizÄciju pieejamu un intuitÄ«vu: NumPy.
NumPy: ZinÄtniskÄs skaitļoÅ”anas pamats PythonÄ
NumPy ir pamata pakete skaitliskai skaitļoÅ”anai PythonÄ. TÄs galvenÄ funkcija ir jaudÄ«gais N-dimensiju masÄ«va objekts, `ndarray`. NumPy patiesÄ burvÄ«ba ir tÄ, ka tÄs vissvarÄ«gÄkÄs rutÄ«nas (matemÄtiskÄs operÄcijas, masÄ«vu manipulÄcijas utt.) nav rakstÄ«tas PythonÄ. TÄs ir ļoti optimizÄtas, iepriekÅ” kompilÄtas C vai Fortran koda, kas ir saistÄ«ts ar zema lÄ«meÅa bibliotÄkÄm, piemÄram, BLAS (Basic Linear Algebra Subprograms) un LAPACK (Linear Algebra Package). Å Ä«s bibliotÄkas bieži tiek pielÄgotas piegÄdÄtÄjam, lai optimÄli izmantotu SIMD instrukciju kopumus, kas pieejami saimniekdatora CPU.
Kad jÅ«s rakstÄt `C = A + B` NumPy, jÅ«s neizpildÄt Python ciklu. JÅ«s nosÅ«tat vienu komandu ļoti optimizÄtai C funkcijai, kas veic saskaitīŔanu, izmantojot SIMD instrukcijas.
Praktisks piemÄrs: no Python cikla uz NumPy masÄ«vu
ApskatÄ«sim to darbÄ«bÄ. MÄs saskaitÄ«sim divus lielus skaitļu masÄ«vus, vispirms ar tÄ«ru Python ciklu un pÄc tam ar NumPy. JÅ«s varat palaist Å”o kodu Jupyter Notebook vai Python skriptÄ, lai redzÄtu rezultÄtus savÄ maŔīnÄ.
Vispirms mÄs sagatavojam datus:
import time
import numpy as np
# Let's use a large number of elements
num_elements = 10_000_000
# Pure Python lists
list_a = [i * 0.5 for i in range(num_elements)]
list_b = [i * 0.2 for i in range(num_elements)]
# NumPy arrays
array_a = np.arange(num_elements) * 0.5
array_b = np.arange(num_elements) * 0.2
Tagad mÄrÄ«sim tÄ«rÄ Python cikla laiku:
start_time = time.time()
result_list = [0] * num_elements
for i in range(num_elements):
result_list[i] = list_a[i] + list_b[i]
end_time = time.time()
python_duration = end_time - start_time
print(f"Pure Python loop took: {python_duration:.6f} seconds")
Un tagad lÄ«dzvÄrtÄ«gÄ NumPy operÄcija:
start_time = time.time()
result_array = array_a + array_b
end_time = time.time()
numpy_duration = end_time - start_time
print(f"NumPy vectorized operation took: {numpy_duration:.6f} seconds")
# Calculate the speedup
if numpy_duration > 0:
print(f"NumPy is approximately {python_duration / numpy_duration:.2f}x faster.")
ParastÄ modernÄ maŔīnÄ izvade bÅ«s satriecoÅ”a. JÅ«s varat sagaidÄ«t, ka NumPy versija bÅ«s no 50 lÄ«dz 200 reizÄm ÄtrÄka. TÄ nav neliela optimizÄcija; tÄ ir fundamentÄla izmaiÅa aprÄÄ·inu veikÅ”anas veidÄ.
UniversÄlÄs funkcijas (ufuncs): NumPy Ätruma dzinÄjs
Tikko veiktÄ operÄcija (`+`) ir NumPy universÄlÄs funkcijas jeb ufunc piemÄrs. TÄs ir funkcijas, kas darbojas ar `ndarray` elementu pa elementam veidÄ. TÄs ir NumPy vektorizÄtÄs jaudas kodols.
Ufunc piemÄri ietver:
- MatemÄtiskÄs operÄcijas: `np.add`, `np.subtract`, `np.multiply`, `np.divide`, `np.power`.
- TrigonometriskÄs funkcijas: `np.sin`, `np.cos`, `np.tan`.
- LoÄ£iskÄs operÄcijas: `np.logical_and`, `np.logical_or`, `np.greater`.
- EksponenciÄlÄs un logaritmiskÄs funkcijas: `np.exp`, `np.log`.
JÅ«s varat apvienot Ŕīs operÄcijas, lai izteiktu sarežģītas formulas, nekad nerakstot eksplicitÄtu ciklu. Apsveriet Gausa funkcijas aprÄÄ·inÄÅ”anu:
# x is a NumPy array of a million points
x = np.linspace(-5, 5, 1_000_000)
# Scalar approach (very slow)
result = []
for val in x:
term = -0.5 * (val ** 2)
result.append((1 / np.sqrt(2 * np.pi)) * np.exp(term))
# Vectorized NumPy approach (extremely fast)
result_vectorized = (1 / np.sqrt(2 * np.pi)) * np.exp(-0.5 * x**2)
VektorizÄtÄ versija ir ne tikai dramatiski ÄtrÄka, bet arÄ« kodolÄ«gÄka un lasÄmÄka tiem, kas ir pazÄ«stami ar skaitlisko skaitļoÅ”anu.
PÄrsniedzot pamatus: Apraide un atmiÅas izkÄrtojums
NumPy vektorizÄcijas iespÄjas vÄl vairÄk uzlabo jÄdziens, ko sauc par apraidi (broadcasting). Tas apraksta, kÄ NumPy apstrÄdÄ masÄ«vus ar dažÄdÄm formÄm aritmÄtisko operÄciju laikÄ. Apraide ļauj veikt operÄcijas starp lielu masÄ«vu un mazÄku (piemÄram, skalÄru), neuztverot mazÄkÄ masÄ«va kopijas, lai tÄs atbilstu lielÄkÄ masÄ«va formai. Tas ietaupa atmiÅu un uzlabo veiktspÄju.
PiemÄram, lai katru masÄ«va elementu mÄrogotu ar koeficientu 10, jums nav jÄizveido masÄ«vs, kas pilns ar 10. JÅ«s vienkÄrÅ”i rakstÄt:
my_array = np.array([1, 2, 3, 4])
scaled_array = my_array * 10 # Broadcasting the scalar 10 across my_array
TurklÄt datu izkÄrtojums atmiÅÄ ir kritiski svarÄ«gs. NumPy masÄ«vi tiek glabÄti blakus esoÅ”Ä atmiÅas blokÄ. Tas ir bÅ«tiski SIMD, kas prasa datus secÄ«gi ielÄdÄt tÄs plaÅ”ajos reÄ£istros. AtmiÅas izkÄrtojuma (piemÄram, C stila rindu-galvenÄ pret Fortran stila kolonnu-galvenÄ) izpratne kļūst svarÄ«ga uzlabotai veiktspÄjas pielÄgoÅ”anai, Ä«paÅ”i strÄdÄjot ar daudzdimensiju datiem.
Robežu pÄrsniegÅ”ana: Uzlabotas SIMD bibliotÄkas
NumPy ir pirmais un vissvarÄ«gÄkais rÄ«ks vektorizÄcijai PythonÄ. TomÄr, kas notiek, ja jÅ«su algoritmu nevar viegli izteikt, izmantojot standarta NumPy ufuncs? IespÄjams, jums ir cikls ar sarežģītu nosacÄ«juma loÄ£iku vai pielÄgotu algoritmu, kas nav pieejams nevienÄ bibliotÄkÄ. Å eit nÄk talkÄ uzlabotÄki rÄ«ki.
Numba: Just-In-Time (JIT) kompilÄcija Ätrumam
Numba ir ievÄrojama bibliotÄka, kas darbojas kÄ Just-In-Time (JIT) kompilators. TÄ nolasa jÅ«su Python kodu un izpildes laikÄ to pÄrvÄrÅ” ļoti optimizÄtÄ maŔīnkodÄ, jums nekad neatstÄjot Python vidi. TÄ ir Ä«paÅ”i izcila ciklu optimizÄÅ”anÄ, kas ir standarta Python galvenÄ vÄjÄ«ba.
VisbiežÄk Numba tiek izmantota, izmantojot tÄs dekoratoru `@jit`. Å emsim piemÄru, ko ir grÅ«ti vektorizÄt NumPy: pielÄgotu simulÄcijas ciklu.
import numpy as np
from numba import jit
# A hypothetical function that is hard to vectorize in NumPy
def simulate_particles_python(positions, velocities, steps):
for _ in range(steps):
for i in range(len(positions)):
# Some complex, data-dependent logic
if positions[i] > 0:
velocities[i] -= 9.8 * 0.01
else:
velocities[i] = -velocities[i] * 0.9 # Inelastic collision
positions[i] += velocities[i] * 0.01
return positions
# The exact same function, but with the Numba JIT decorator
@jit(nopython=True, fastmath=True)
def simulate_particles_numba(positions, velocities, steps):
for _ in range(steps):
for i in range(len(positions)):
if positions[i] > 0:
velocities[i] -= 9.8 * 0.01
else:
velocities[i] = -velocities[i] * 0.9
positions[i] += velocities[i] * 0.01
return positions
VienkÄrÅ”i pievienojot `@jit(nopython=True)` dekoratoru, jÅ«s sakÄt Numba, lai kompilÄtu Å”o funkciju maŔīnkodÄ. Arguments `nopython=True` ir kritiski svarÄ«gs; tas nodroÅ”ina, ka Numba Ä£enerÄ kodu, kas neizmanto lÄno Python interpretatoru. Karogs `fastmath=True` ļauj Numba izmantot mazÄk precÄ«zas, bet ÄtrÄkas matemÄtiskÄs operÄcijas, kas var nodroÅ”inÄt automÄtisko vektorizÄciju. Kad Numba kompilators analizÄs iekÅ”Äjo ciklu, tas bieži spÄs automÄtiski Ä£enerÄt SIMD instrukcijas, lai apstrÄdÄtu vairÄkas daļiÅas vienlaicÄ«gi, pat ar nosacÄ«juma loÄ£iku, tÄdÄjÄdi nodroÅ”inot veiktspÄju, kas lÄ«dzinÄs vai pat pÄrsniedz ar roku rakstÄ«ta C koda veiktspÄju.
Cython: Python apvienoŔana ar C/C++
Pirms Numba kļuva populÄra, Cython bija galvenais rÄ«ks Python koda paÄtrinÄÅ”anai. Cython ir Python valodas virskopa, kas atbalsta arÄ« C/C++ funkciju izsaukÅ”anu un C tipu deklarÄÅ”anu mainÄ«gajiem un klases atribÅ«tiem. TÄ darbojas kÄ pirmsizpildes (AOT) kompilators. JÅ«s rakstÄt savu kodu `.pyx` failÄ, ko Cython kompilÄ C/C++ avota failÄ, kas pÄc tam tiek kompilÄts standarta Python paplaÅ”inÄjuma modulÄ«.
Cython galvenÄ priekÅ”rocÄ«ba ir smalkÄ kontrole, ko tÄ nodroÅ”ina. Pievienojot statiskÄs tipu deklarÄcijas, jÅ«s varat novÄrst lielu daļu Python dinamisko izmaksu.
VienkÄrÅ”a Cython funkcija var izskatÄ«ties Å”Ädi:
# In a file named 'sum_module.pyx'
def sum_typed(long[:] arr):
cdef long total = 0
cdef int i
for i in range(arr.shape[0]):
total += arr[i]
return total
Å eit `cdef` tiek izmantots C lÄ«meÅa mainÄ«go (`total`, `i`) deklarÄÅ”anai, un `long[:]` nodroÅ”ina tipizÄtu atmiÅas skatu uz ievades masÄ«vu. Tas ļauj Cython Ä£enerÄt ļoti efektÄ«vu C ciklu. Ekspertiem Cython pat nodroÅ”ina mehÄnismus tieÅ”ai SIMD iekÅ”Äjo elementu izsaukÅ”anai, piedÄvÄjot augstÄko kontroles lÄ«meni veiktspÄjas kritiskiem lietojumiem.
SpecializÄtÄs bibliotÄkas: ieskats ekosistÄmÄ
Augstas veiktspÄjas Python ekosistÄma ir plaÅ”a. Papildus NumPy, Numba un Cython pastÄv citi specializÄti rÄ«ki:
- NumExpr: Ätrs skaitlisko izteiksmju novÄrtÄtÄjs, kas dažkÄrt var pÄrspÄt NumPy, optimizÄjot atmiÅas izmantoÅ”anu un izmantojot vairÄkus kodolus, lai novÄrtÄtu izteiksmes, piemÄram, `2*a + 3*b`.
- Pythran: Pirmsizpildes (AOT) kompilators, kas tulko Python koda apakÅ”kopu, Ä«paÅ”i kodu, kas izmanto NumPy, ļoti optimizÄtÄ C++11, bieži nodroÅ”inot agresÄ«vu SIMD vektorizÄciju.
- Taichi: DomÄnam specifiska valoda (DSL), kas iebÅ«vÄta PythonÄ augstas veiktspÄjas paralÄlai skaitļoÅ”anai, Ä«paÅ”i populÄra datorgrafikÄ un fizikas simulÄcijÄs.
Praktiskie apsvÄrumi un labÄkÄ prakse globÄlai auditorijai
Augstas veiktspÄjas koda rakstīŔana ietver vairÄk nekÄ tikai pareizas bibliotÄkas izmantoÅ”anu. Å eit ir dažas vispÄrÄji piemÄrojamas labÄkÄs prakses.
KÄ pÄrbaudÄ«t SIMD atbalstu
VeiktspÄja, ko iegÅ«stat, ir atkarÄ«ga no aparatÅ«ras, uz kuras darbojas jÅ«su kods. Bieži ir noderÄ«gi zinÄt, kÄdi SIMD instrukciju kopumi tiek atbalstÄ«ti noteiktÄ CPU. JÅ«s varat izmantot starpplatformu bibliotÄku, piemÄram, `py-cpuinfo`.
# Install with: pip install py-cpuinfo
import cpuinfo
info = cpuinfo.get_cpu_info()
supported_flags = info.get('flags', [])
print("SIMD Support:")
if 'avx512f' in supported_flags:
print("- AVX-512 supported")
elif 'avx2' in supported_flags:
print("- AVX2 supported")
elif 'avx' in supported_flags:
print("- AVX supported")
elif 'sse4_2' in supported_flags:
print("- SSE4.2 supported")
else:
print("- Basic SSE support or older.")
Tas ir bÅ«tiski globÄlÄ kontekstÄ, jo mÄkoÅdatoÅ”anas instances un lietotÄju aparatÅ«ra var ievÄrojami atŔķirties dažÄdos reÄ£ionos. AparatÅ«ras iespÄju pÄrzinÄÅ”ana var palÄ«dzÄt jums izprast veiktspÄjas Ä«paŔības vai pat kompilÄt kodu ar specifiskÄm optimizÄcijÄm.
Datu tipu nozīme
SIMD operÄcijas ir ļoti specifiskas datu tipiem (`dtype` NumPy). JÅ«su SIMD reÄ£istra platums ir fiksÄts. Tas nozÄ«mÄ, ka, ja izmantojat mazÄku datu tipu, varat ievietot vairÄk elementu vienÄ reÄ£istrÄ un apstrÄdÄt vairÄk datu vienÄ instrukcijÄ.
PiemÄram, 256 bitu AVX reÄ£istrs var glabÄt:
- Äetrus 64 bitu peldoÅ”Ä punkta skaitļus (`float64` vai `double`).
- AstoÅus 32 bitu peldoÅ”Ä punkta skaitļus (`float32` vai `float`).
Ja jÅ«su lietojumprogrammas precizitÄtes prasÄ«bas var izpildÄ«t ar 32 bitu peldoÅ”ajiem punktiem, vienkÄrÅ”i mainot NumPy masÄ«vu `dtype` no `np.float64` (daudzÄs sistÄmÄs noklusÄjums) uz `np.float32`, var potenciÄli dubultot jÅ«su skaitļoÅ”anas caurlaides spÄju AVX iespÄjotÄ aparatÅ«rÄ. VienmÄr izvÄlieties mazÄko datu tipu, kas nodroÅ”ina pietiekamu precizitÄti jÅ«su problÄmai.
Kad NEVEKTORIZÄT
VektorizÄcija nav sudraba lode. Ir scenÄriji, kur tÄ ir neefektÄ«va vai pat pretproduktÄ«va:
- No datiem atkarÄ«ga vadÄ«bas plÅ«sma: Cikli ar sarežģītÄm `if-elif-else` atzarÄm, kas ir neparedzamas un noved pie atŔķirÄ«gÄm izpildes ceļiem, kompilatoriem ir ļoti grÅ«ti automÄtiski vektorizÄt.
- SecÄ«gas atkarÄ«bas: Ja viena elementa aprÄÄ·ins ir atkarÄ«gs no iepriekÅ”ÄjÄ elementa rezultÄta (piemÄram, dažÄs rekursÄ«vÄs formulÄs), problÄma ir pÄc bÅ«tÄ«bas secÄ«ga un to nevar paralelizÄt ar SIMD.
- Mazi datu kopumi: Ä»oti maziem masÄ«viem (piemÄram, mazÄk nekÄ duci elementu) vektorizÄtÄs funkcijas izsaukÅ”anas iestatīŔanas izmaksas NumPy var bÅ«t lielÄkas nekÄ vienkÄrÅ”a, tieÅ”a Python cikla izmaksas.
- NeregulÄra atmiÅas piekļuve: Ja jÅ«su algoritms prasa pÄrvietoties atmiÅÄ neparedzamÄ veidÄ, tas sagrauj CPU keÅ”atmiÅas un iepriekÅ”Äjas ielÄdes mehÄnismus, anulÄjot galveno SIMD ieguvumu.
GadÄ«juma izpÄte: AttÄlu apstrÄde ar SIMD
NostiprinÄsim Å”os jÄdzienus ar praktisku piemÄru: krÄsu attÄla pÄrvÄrÅ”anu pelÄktoÅos. AttÄls ir tikai 3D skaitļu masÄ«vs (augstums x platums x krÄsu kanÄli), padarot to par perfektu kandidÄtu vektorizÄcijai.
Standarta formula spilgtumam ir: `PelÄktoÅi = 0.299 * R + 0.587 * G + 0.114 * B`.
PieÅemsim, ka mums ir attÄls, kas ielÄdÄts kÄ NumPy masÄ«vs ar formu `(1920, 1080, 3)` un datu tipu `uint8`.
1. metode: TÄ«rs Python cikls (LÄnais veids)
def to_grayscale_python(image):
h, w, _ = image.shape
grayscale_image = np.zeros((h, w), dtype=np.uint8)
for r in range(h):
for c in range(w):
pixel = image[r, c]
gray_value = 0.299 * pixel[0] + 0.587 * pixel[1] + 0.114 * pixel[2]
grayscale_image[r, c] = int(gray_value)
return grayscale_image
Tas ietver trÄ«s ligzdotus ciklus un bÅ«s neticami lÄns augstas izŔķirtspÄjas attÄliem.
2. metode: NumPy vektorizÄcija (Ätrais veids)
def to_grayscale_numpy(image):
# Define weights for R, G, B channels
weights = np.array([0.299, 0.587, 0.114])
# Use dot product along the last axis (the color channels)
grayscale_image = np.dot(image[...,:3], weights).astype(np.uint8)
return grayscale_image
Å ajÄ versijÄ mÄs veicam punktu reizinÄjumu. NumPy `np.dot` ir ļoti optimizÄta un izmantos SIMD, lai reizinÄtu un summÄtu R, G, B vÄrtÄ«bas daudziem pikseļiem vienlaicÄ«gi. VeiktspÄjas atŔķirÄ«ba bÅ«s milzÄ«ga ā viegli 100x vai vairÄk Ätruma pieaugums.
NÄkotne: SIMD un Python mainÄ«gÄ ainava
Augstas veiktspÄjas Python pasaule nepÄrtraukti attÄ«stÄs. BÄdÄ«gi slavenais globÄlais interpretatora bloÄ·ÄtÄjs (GIL), kas neļauj vairÄkiem pavedieniem paralÄli izpildÄ«t Python baitkodu, tiek apÅ”aubÄ«ts. Projekti, kuru mÄrÄ·is ir padarÄ«t GIL neobligÄtu, varÄtu pavÄrt jaunus paralÄluma ceļus. TomÄr SIMD darbojas apakÅ”puses lÄ«menÄ« un to neietekmÄ GIL, padarot to par uzticamu un nÄkotnes noturÄ«gu optimizÄcijas stratÄÄ£iju.
TÄ kÄ aparatÅ«ra kļūst daudzveidÄ«gÄka, ar specializÄtiem paÄtrinÄtÄjiem un jaudÄ«gÄkÄm vektoru vienÄ«bÄm, rÄ«ki, kas abstrahÄ aparatÅ«ras detaļas, vienlaikus nodroÅ”inot veiktspÄju ā piemÄram, NumPy un Numba ā kļūs vÄl svarÄ«gÄki. NÄkamais solis pÄc SIMD CPU ietvaros bieži ir SIMT (Single Instruction, Multiple Threads) GPU, un tÄdas bibliotÄkas kÄ CuPy (NumPy aizstÄjÄjs NVIDIA GPU) piemÄro Å”os paÅ”us vektorizÄcijas principus vÄl masÄ«vÄkÄ mÄrogÄ.
SecinÄjums: PieÅemiet vektoru
MÄs esam ceļojuÅ”i no CPU kodola lÄ«dz Python augsta lÄ«meÅa abstrakcijÄm. GalvenÄ atziÅa ir tÄda, ka, lai rakstÄ«tu Ätru skaitlisko kodu PythonÄ, jums ir jÄdomÄ masÄ«vos, nevis ciklos. TÄ ir vektorizÄcijas bÅ«tÄ«ba.
Apkoposim mūsu ceļojumu:
- ProblÄma: TÄ«ri Python cikli ir lÄni skaitliskiem uzdevumiem interpretatora izmaksu dÄļ.
- AparatÅ«ras risinÄjums: SIMD ļauj vienam CPU kodolam vienlaicÄ«gi veikt vienu un to paÅ”u operÄciju ar vairÄkiem datu punktiem.
- Galvenais Python rÄ«ks: NumPy ir vektorizÄcijas stÅ«rakmens, nodroÅ”inot intuitÄ«vu masÄ«vu objektu un bagÄtÄ«gu ufunc bibliotÄku, kas izpildÄs kÄ optimizÄts, SIMD iespÄjots C/Fortran kods.
- Uzlabotie rÄ«ki: PielÄgotiem algoritmiem, kas nav viegli izsakÄmi NumPy, Numba nodroÅ”ina JIT kompilÄciju, lai automÄtiski optimizÄtu jÅ«su ciklus, savukÄrt Cython piedÄvÄ smalku kontroli, apvienojot Python ar C.
- DomÄÅ”anas veids: EfektÄ«vai optimizÄcijai nepiecieÅ”ama datu tipu, atmiÅas modeļu izpratne un pareizÄ rÄ«ka izvÄle konkrÄtam uzdevumam.
NÄkamreiz, kad rakstÄ«siet `for` ciklu, lai apstrÄdÄtu lielu skaitļu sarakstu, apstÄjieties un pajautÄjiet: "Vai es varu to izteikt kÄ vektora operÄciju?" PieÅemot Å”o vektorizÄto domÄÅ”anas veidu, jÅ«s varat atraisÄ«t mÅ«sdienu aparatÅ«ras patieso veiktspÄju un pacelt savas Python lietojumprogrammas jaunÄ Ätruma un efektivitÄtes lÄ«menÄ«, neatkarÄ«gi no tÄ, kurÄ pasaules malÄ jÅ«s kodÄjat.